package com.star.io.netty.socket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;  
import io.netty.channel.nio.NioEventLoopGroup;  
import io.netty.channel.socket.SocketChannel;  
import io.netty.channel.socket.nio.NioServerSocketChannel;  
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;  
import io.netty.handler.codec.LengthFieldPrepender;  
import io.netty.buffer.ByteBuf;  
import io.netty.buffer.Unpooled;  
  
public class NettyServer {  
    private final int port;  
  
    public NettyServer(int port) {  
        this.port = port;  
    }  
  
    public void start() throws InterruptedException {  
        EventLoopGroup bossGroup = new NioEventLoopGroup();  
        EventLoopGroup workerGroup = new NioEventLoopGroup();  
        try {  
            ServerBootstrap b = new ServerBootstrap();  
            b.group(bossGroup, workerGroup)  
             .channel(NioServerSocketChannel.class)  
             .childHandler(new ChannelInitializer<SocketChannel>() {  
                 @Override  
                 public void initChannel(SocketChannel ch) throws Exception {  
                     ChannelPipeline p = ch.pipeline();  
                     // 解决粘包拆包问题，LengthFieldBasedFrameDecoder 需要知道消息的最大长度、长度字段的偏移量和长度字段的长度   1MB
                     // p.addLast(new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
                     /* lengthAdjustment：-7 指定了一个调整值，它会被加到长度字段的值上，以得到实际的消息内容长度
                        由于长度字段只表示数据部分的长度，我们需要调整这个值以包括起始码、控制字、校验码和结束码的长度
                        initialBytesToStrip：0，指定了在将帧传递给下一个处理器之前，需要从帧中移除的字节数
                      */
                     p.addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 4));
                     // p.addLast(new CustomFrameDecoder());
                     // 在发送的消息前添加长度字段
                     p.addLast(new LengthFieldPrepender(4));
                     // 在这的消息已经经过上面handler的处理，已经解决了粘包问题
                     p.addLast(new ServerHandler());  
                 }  
             });  
  
            ChannelFuture f = b.bind(port).sync();
            System.out.println( port + "：服务端启动成功");
            f.channel().closeFuture().sync();  
        } finally {  
            bossGroup.shutdownGracefully();  
            workerGroup.shutdownGracefully();  
        }  
    }  
  
    public static void main(String[] args) throws InterruptedException {  
        new NettyServer(8088).start();
    }  
}  
  
class ServerHandler extends SimpleChannelInboundHandler<ByteBuf> {  
    @Override  
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {  
        ProtoPacket packet = ProtoPacket.decode(msg);
        System.out.println("Server received: " + packet);
        // 回复客户端
        ProtoPacket response = new ProtoPacket("Server", (byte) 1, "Hello from server");
        ctx.writeAndFlush(response.encode(ctx.alloc()));  
    }  
  
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {  
        cause.printStackTrace();  
        ctx.close();  
    }  
}